import logging
import re
import uuid
import sys
import os
import urllib2
import HTMLParser


class Error(Exception):
    """Base class for exceptions in this module."""
    pass
    
from google.appengine.api        import memcache
from google.appengine.ext        import webapp
from google.appengine.ext        import db
from google.appengine.ext.webapp import template
from model                       import Mech

from BeautifulSoup               import BeautifulSoup

logging.getLogger().setLevel(logging.DEBUG)

    
class IndexHandler(webapp.RequestHandler):

    def get(self):
        self.response.out.write(template.render("templates/index.html", self.process_get() ))
 
    def process_get(self):
        logging.error("uuu")
        logging.info("ggg")    

        dropid  = self.request.get('d')
        home    = True
        action  = self.request.get('a')

        if ( dropid ):
            mechlist = self.load_mechlist(dropid)

            if action == "x":
                for m in mechlist:
                    if m.player == player and m.player != self.find_starter(mechlist):
                        m.delete()
                        mechlist.remove(m)
                        self.update_cache(dropid, mechlist)
                        self.redirect("/?d="+dropid)

        return {
            'starter': '',
            'dropid': dropid,
            'proposed_dropid': uuid.uuid4().hex,
            'mechs':  None,
            'method': 'get',
            'home': home,
            'allmechs': self.sorted_list_of_all_mechs(),
            'mech':    {'dropid': dropid, 'player': ''},
            'drop_totals': { 'active_players': 0, 'active_tonnage': 0, 'total_players':  0, 'total_tonnage':  0 }
        }


    def post(self):
        e = self.request.get('e', default_value="");
        t = "templates/roster.html" if e else "templates/index.html"
        self.response.out.write(template.render(t, self.process_post() ))

    def process_post(self):
        dropid         = self.request.get('d', default_value=""); 
        player         = self.request.get('p', default_value=""); 
        mechtype       = self.request.get('m', default_value="")
        name           = self.request.get('n', default_value="")
        speed          = self.request.get('sp', default_value=0) 
        action         = self.request.get('a', default_value="")
        players        = self.request.get('players', default_value="")
        weapons        = self.request.get('w', default_value="")
        bombs          = self.request.get("ebombs", default_value=0)
        artillery      = self.request.get("eartillery", default_value=0)
        artemis        = self.request.get("eartemis", default_value=0)
        ams            = self.request.get("eams", default_value=0)
        ecm            = self.request.get("eecm", default_value=0)
        bap            = self.request.get("ebap", default_value=0)
        uav            = self.request.get("euav", default_value=0)
        other          = self.request.get("eother", default_value=0)
   
        if weapons == "":
            weapons = []
        else:
            weapons = weapons.split(",")
            
        if players == "":
            players = []
        else:
            players = players.split(",")
            
        if not dropid: dropid = uuid.uuid4().hex
        mechlist       = self.load_mechlist(dropid)
        mech           = {'dropid': dropid, 'player': ''}

        if player:
            mechlist = self.process_actions( mechlist, dropid, players, action )
            
            if action != "x":
                mech = self.save_mech(mechlist, dropid, speed, mechtype, name, player, artemis, ams, ecm, bap, uav, bombs, artillery, other, weapons)
                if not mech:
                    mech       = self.add_mech(mechlist, dropid, speed, mechtype, name, player, artemis, ams, ecm, bap, uav, bombs, artillery, other, weapons)
                    mechlist.append(mech)
                    self.update_cache(dropid, mechlist)

        drop_totals    = self.calculate_drop_totals(mechlist)

    
        return {
            'starter': self.find_starter(mechlist),
            'dropid': dropid,
            'mech': mech,
            'method': 'post',
            'mech_classes': ['Light', "Medium", "Heavy", "Assault", "Special"],
            'mechs': mechlist,
            'tonnages': self.tonnages(),
            'tonnages_grouped': self.tonnages_grouped(),
            'allmechs': self.sorted_list_of_all_mechs(),
            'drop_totals': drop_totals,
            'speeds': [40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170],
            'garage':[]
        }
        
    def tonnages(self):
        return self.fetch_mechs_from_smurfy()

    def tonnages_grouped(self):
        
        mechs    = self.fetch_mechs_from_smurfy()
        tonnages = {}
 
        for mech in mechs:
            tons  = mechs[mech]
            klass = ""
        
            if ( tons > 0 and tons < 40 ):
                klass = "Light"
            if ( tons > 35 and tons < 60 ):
                klass = "Medium"
            if ( tons > 55 and tons < 80 ):
                klass = "Heavy"
            if ( tons > 75 and tons < 110 ):
                klass = "Assault"
            
            hyphen = mech.find("-")
            
            if ( hyphen > 0 and mech != "X-5" and mech != "YEN-LO-WANG" ):
                model = mech[0:hyphen]
                variant = mech[hyphen+1:]
            else:
                klass = "Special"
                model = "HERO"
                variant = mech.replace(" ", "-")
            
            if ( not klass in tonnages ):
                tonnages[klass] = {}
                
            if ( not model in tonnages[klass] ):
                tonnages[klass][model] = []
            
            tonnages[klass][model].append(variant)
            tonnages[klass][model].sort()
            
        return tonnages

    #
    # Find the starter
    #
    def find_starter(self, mechlist):
        for m in mechlist:
            if m.starter == 1:
                return m.player

        return ""

    #
    # Process actions like Lance Assignment, Assign lead and Player removal
    #
    def process_actions(self, mechlist, dropid, players, action):

        starter = self.find_starter(mechlist)
        list_changed = 0

        if action and len(players) > 0:
            lance   = ""

            if action == "a":
                lance = "Alpha"
            if action == "b":
                lance = "Bravo"
            if action == "c":
                lance ="Charlie"
            if action == "d":
                lance = "Delta"
            if action == "r":
                lance = "Reserve"
            if action == "l":
                pass
            if action == "x":
                pass

            deleted = []
            for m in mechlist:
                if action == "l":
                    if m.player == players[0]:
                        m.starter = 1
                        mech = m
                    else:
                        m.starter = 0

                    m.put()
                    list_changed = 1

                if action == "x":
                    for p in players:
                        if m.player == p:
                            deleted.append(m.player)
                            m.delete()
                            list_changed = 1

                if lance != "":
                    for p in players:
                        if m.player == p:
                            m.lance = lance
                            m.put()
                            list_changed = 1

            if len(deleted) > 0:
                for d in deleted:
                    for m in mechlist:
                        if d == m.player:
                            mechlist.remove(m)

            if list_changed:
                self.update_cache(dropid, mechlist)

        return mechlist
    #
    # Save changes to an existing mech 
    #
    def save_mech(self, mechlist, dropid, speed, mechtype, name, player, artemis, ams, ecm, bap, uav, bombs, artillery, other, weapons):
        mech  = None
        mechcount = 0
        list_changed = 0

        for m in mechlist:
            mechcount = mechcount +1
            mech_changed = 0

            if m.player == player:

                try:
                    speed = int(speed)
                    if m.speed != speed: 
                        mech_changed = 1
                        list_changed = 1

                    m.speed = speed
                except:
                    pass
                
                if m.mechtype != mechtype: 
                    mech_changed = 1
                    list_changed = 1

                m.mechtype = mechtype

                if name != "":
                    if m.name != name: 
                        mech_changed = 1
                        list_changed = 1

                    m.name = name
                
              
                if m.ecm != ecm: 
                    mech_changed = 1
                    list_changed = 1

                m.ecm = int(ecm)
                
            
                if m.uav != uav: 
                    mech_changed = 1
                    list_changed = 1

                m.uav = int(uav)
  
                
            
                if m.bap != bap: 
                    mech_changed = 1
                    list_changed = 1

                m.bap = int(bap)
                
                
   
                if m.artemis != artemis: 
                    mech_changed = 1
                    list_changed = 1

                m.artemis = int(artemis)
                
                if m.ams != ams: 
                    mech_changed = 1
                    list_changed = 1

                m.ams = int(ams)
                
         
                if m.bombs != bombs: 
                    mech_changed = 1
                    list_changed = 1
                
                m.bombs = int(bombs)                
          
                if m.artillery != artillery: 
                    mech_changed = 1
                    list_changed = 1
                    
                m.artillery = int(artillery)
                
                if len(m.weapons) != len(weapons): 
                
                    mech_changed = 1
                    list_changed = 1
                    
                m.weapons = weapons
                        
                if ( other != None ):
                    if m.other != other: 
                        mech_changed = 1
                        list_changed = 1

                    m.other = int(other)

                mech = m

            
            if mech_changed: 
                mech.put()

        # nasty side effect
        if list_changed: 
            self.update_cache(dropid, mechlist)

        return mech

        
    def mechmodel(self, mechtype):
        return mechtype[0:mechtype.find("-")]
    #
    # Add a new mech 
    #
    def add_mech(self, mechlist, dropid, speed, mechtype, name, player, artemis, ams, ecm, bap, uav, bombs, artillery, other, weapons):
        mechcount = len(mechlist)
        mech = Mech(
            dropid=dropid, speed=speed, mechtype=mechtype, name=name,
            player=player, position = mechcount + 1, 
            lance = "Reserve", weapons = weapons,
            artillery=artillery, bombs=bombs, artemis=artemis, ams=ams, ecm=ecm, bap=bap, uav=uav, other=other,
        )

        if mechcount == 0:
            starter = player
            mech.starter = 1

        mech.put()

        return mech

    # 
    # A list of all mechs for display in the html-form
    #
    def sorted_list_of_all_mechs(self):
        mechlist = self.fetch_mechs_from_smurfy()
        allmechs = []
        
        for key in sorted(mechlist.iterkeys()):
            allmechs.append(key)
        
        return allmechs
        
    #
    # Get a list of all mechs and tonnage from the smurfy website,
    # cache for a day to avoid spamming
    #
    def fetch_mechs_from_smurfy(self):
    
        mechlist = memcache.get("smurfy")
        htmlparser = HTMLParser.HTMLParser()
        
        if (mechlist is not None):
            return mechlist
        
        mechlist = {}
        tonnage = 0
        
        try:
            html = urllib2.urlopen("http://mwo.smurfy-net.de/")
        except urllib2.URLError, e:
            pass
            
        if html:
            logging.debug(html)
            soup = BeautifulSoup(html)
            for row in soup.findAll('tr'):
                if row.th:
                    tclass = row.th.get("class")
                    if tclass:
                        if u'mechs_family' == tclass:
                            tonnage = row.th.small.string
                            tonnage = int( re.sub("[\\(\\)]", "", tonnage) )
                
                if row.td:
                    link = row.td.a
                    if link:
                        href = link.get('href')
                        text = htmlparser.unescape(str(link.string))
                        text = text.replace("'", u"\u2019")
                        text = text.replace("&#039;", u"\u2019")
                        text = text.replace(" ", u"\u2000")
                                
                        
                        if href and re.match("/mechlab#i=", href):
                            if ( text.find("(C)") == -1 ) and ( text.find("(S)") == -1 ) and ( text.find("(F)") == -1 ):
                                mechlist[text] = tonnage
                            
            # mechlist[""] = 0
            
            memcache.set("smurfy", mechlist, 24 * 60 * 60)
         
        return mechlist
        
    #
    # Calculate tonnage totals
    #
    def calculate_drop_totals(self, mechlist):
        active_players = 0
        active_tonnage = 0
        total_players  = 0
        total_tonnage  = 0
        
        tonnages = self.fetch_mechs_from_smurfy()

        for m in mechlist:
            if m.lance:
                total_players = total_players + 1
                if m.mechtype:
                    total_tonnage = total_tonnage + tonnages[m.mechtype]
                if m.lance != "Reserve":
                    active_players = active_players + 1
                    if m.mechtype:
                        active_tonnage = active_tonnage + tonnages[m.mechtype]

        return {
            'active_players': active_players,
            'active_tonnage': active_tonnage,
            'total_players':  total_players,
            'total_tonnage':  total_tonnage
        }

    def invalidate_cache(self, dropid):
        memcache.delete(dropid)

    def update_cache(self, dropid, list):
        memcache.set(dropid, list, 5 * 60)

    def load_mechlist(self, dropid):
        list = [ "e" ]
        
        try:
            list = memcache.get(dropid)
        except:
            pass
        
        if list is not None:
            return list

        
        query = db.GqlQuery('SELECT * FROM Mech WHERE dropid = :1 ORDER BY lance', dropid)
        list = []

        for i in query:
            list.append(i)

        try:
            memcache.add(dropid, list, 5 * 60)
        except:
            pass
            
        return list
        
class JsonHandler(IndexHandler):

    def jsont(self, jsont):
        json = jsont
        json = re.sub(r'^ +', "", json, 0, re.MULTILINE)
        json = re.sub(r' +$', "", json, 0, re.MULTILINE)
        json = re.sub(r' *: +', ":", json, 0, re.MULTILINE)
        json = re.sub(r'\n', "", json, 0, re.MULTILINE)
        json = re.sub(r'\r', "", json, 0, re.MULTILINE)
        json = re.sub(r',\]', "]", json, 0, re.MULTILINE)
        json = re.sub(r',\}', "}", json, 0, re.MULTILINE)
    
        return json
    
    def post(self):
        self.response.headers["Content-Type"] = "application/json"
        json = self.jsont( unicode(template.render("templates/dropdata.json", self.process_post())) )
        self.response.out.write( json )
        
    def get(self):
        self.response.headers["Content-Type"] = "application/json"
        json = self.jsont( unicode(template.render("templates/dropdata.json", self.process_get())) )
        self.response.out.write( json )
        
class GarageHandler(webapp.RequestHandler):

    def post(self):
        self.response.out.write( unicode(template.render("templates/garagelist.html", { "garage": self.process() } )))
        
    def get(self):
        self.response.out.write( unicode(template.render("templates/garagelist.html", { "garage": self.process() } )))
        
    def process(self):
        player = self.request.get('p', default_value=""); 
        
        query = db.GqlQuery('SELECT * FROM Mech WHERE player = :1 AND name != :2 ORDER BY name', player, "")
        list = []

        for i in query:
            list.append(i)

        return list


class UnknownHandler(webapp.RequestHandler):

    def get(self):
        self.response.out.write(404)
        self.error(404)

class ForbiddenHandler(webapp.RequestHandler):

    def get(self):
        self.response.out.write(400)
        self.error(400)